import webview
import yaml  # pip install pyyaml 32位python使用
import os
import copy
import traceback
import subprocess
import json
import platform
import requests, datetime

window: None | webview.Window = None


def read_from_yaml(file: str, encoding="utf8"):
    """
    不能删除，因为base模块在使用

    :param file:
    :param encoding:
    :return:
    """
    if not os.path.exists(file):  # 文件不存在，返回空字典
        return {}
    with open(file, 'r', encoding=encoding) as f:
        content = f.read()
    """
    Loader的几种加载方式 
    BaseLoader--仅加载最基本的YAML 
    SafeLoader--安全地加载YAML语言的子集。建议用于加载不受信任的输入。 
    FullLoader--加载完整的YAML语言。避免任意代码执行。这是当前（PyYAML 5.1）默认加载器调用 
            yaml.load(input)（发出警告后）。
    UnsafeLoader--（也称为Loader向后兼容性）原始的Loader代码，可以通过不受信任的数据输入轻松利用。"""
    try:
        obj = yaml.load(content, Loader=yaml.FullLoader)
    except:
        traceback.print_exc()
    return obj


def get_version(app='python'):
    try:
        # 使用subprocess模块运行conda命令来获取版本信息
        result = subprocess.run([app, '--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True,
                                check=True)
        # 输出conda的版本信息，这通常也反映了Anaconda的安装版本
        return result.stdout.strip()
    except (subprocess.CalledProcessError, FileNotFoundError):
        # 如果conda命令不存在或运行失败，则返回None或适当的错误信息
        return None  # 或者你可以抛出一个异常，或者返回一个错误消息字符串


def custom_logic(window):
    window.toggle_fullscreen()
    window.evaluate_js('alert("Nice one brother")')


class YkDict(dict):
    def __init__(self, data_dict):
        super(YkDict, self).__init__(data_dict)

    def get_settings(self, item):
        settings = copy.deepcopy(self)
        if item is not None:
            item_list = item.split('.')
            for i in item_list:
                if isinstance(settings, list):
                    return YkDict({})
                settings = settings.get(i)
                if settings is None:
                    settings = {}
                    break
        if isinstance(settings, dict):
            return YkDict(settings)
        else:
            return settings


def get_settings(item: str = None, setting_file="default"):
    """
    从配置文件中加载配置，优先加载"settings.yml"

    :param item: 使用 mysql.port 类型的字符串指定需要查询的配置项，如果不设置，则返回整个yaml文件的字典
    :param setting_file: 默认配置文件为项目根目录下的settings.yml或settings.yaml，目前不支持更改配置文件，强行修改可能会导致某些配置失效
    :return: 配置项的字典，如果具体到项，则返回的是字符串
    """
    if setting_file == "default":
        setting_file = "settings.yml" if os.path.exists("settings.yml") else None
        setting_file = setting_file or "settings.yaml"

        settings = read_from_yaml(setting_file) or {}
    else:
        settings = read_from_yaml(setting_file) or {}

    if item is not None:
        item_list = item.split('.')
        for i in item_list:
            settings = settings.get(i)
            if settings is None:
                settings = {}
                break
    if isinstance(settings, dict):
        return YkDict(settings)
    else:
        return settings


def check_file_exists(file_path):
    """检查指定路径的文件是否存在"""
    return os.path.exists(file_path)


def get_file_size(file_path):
    """获取已下载文件的大小"""
    return os.path.getsize(file_path) if check_file_exists(file_path) else 0


def get_file_range(url, start):
    """请求剩余未下载的文件块"""
    headers = {
        'Range': f'bytes={start}-',
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
    }
    response = requests.get(url, headers=headers, stream=True)
    return response


def find_item(condition_dict: dict, items: list):
    """
    从对象列表中查找满足指定条件的对象，未找到则返回None

    :param items:
    :param condition_dict:
    :return:
    """
    for item in items:
        hit = True
        for k, v in condition_dict.items():
            if item.get(k) != v:
                hit = False
                break
        if hit:
            return item
    return None


class Api:
    def __init__(self):
        self.mute = False

    def save_note(self):
        return "hello from pywebview"

    def get_key(self, key):
        if key == 'platform':
            return platform.system()  # "Windows" | "Linux" | "Darwin"
        elif key == 'arch':
            res = platform.machine()
            if res.lower() == 'amd64':
                res = 'x86_64'
            return res
        elif key == 'anaconda link':
            # 获取anaconda的链接信息
            links = get_settings(setting_file='link_url.yaml')
            return json.dumps(links)
        elif key == 'conda version':
            _ = get_version('conda')
            if _ is None:
                return 'null'
            else:
                return _
        elif key == 'current version':
            py_ver = get_version().lower()
            pf = self.get_key('platform').lower()
            arch = self.get_key('arch')
            pf = f"{pf} {arch}"
            if py_ver is None:
                return json.dumps({'python': 'null', 'anaconda': None, 'url': None, 'platform': pf})

            settings = get_settings(setting_file='link_url.yaml')
            items = settings.get_settings('anaconda.versions')
            ana_ver = ''
            if py_ver.startswith('python 3.8'):
                py_ver = '3.8.8'
                ana_ver = '2021.05'
            elif py_ver.startswith('python 3.9'):
                py_ver = '3.9.13'
                ana_ver = '2022.10'
            elif py_ver.startswith('python 3.10'):
                py_ver = '3.10.9'
                ana_ver = '2023.03'
            elif py_ver.startswith('python 3.11'):
                py_ver = '3.11.4'
                ana_ver = '2023.07'
            elif py_ver.startswith('python 3.12'):
                py_ver = '3.12.9'
                ana_ver = '2024.10'
            ana_ver = f"anaconda3 {ana_ver}"
            url = find_item({"python": py_ver, "conda": ana_ver, "platform": pf}, items).get('url')
            if url is None:
                url = None
            return json.dumps({
                'python': py_ver,
                'conda': ana_ver,
                'url': url,
                'platform': pf,
            })
        return None

    def print_to_term(self, msg: str, kind='info'):
        """

        :param msg: msg中不能包含\等特殊字符串。
        :param kind: 可取值warning info success error system
        :return:
        """
        global window
        if self.mute:
            return
        if isinstance(window, webview.Window):
            # print(window)
            # window.evaluate_js("alert('111')")
            msg = f"{datetime.datetime.now().strftime('%Y%m%d %H:%M:%S')}>{msg}"
            cmd = f'window.debug("{msg}", "{kind}")'
            print(f"{cmd=}")
            window.evaluate_js(
                cmd
                , callback=None)

    def download_file(self, url, file_path):
        """下载文件并显示下载进度，支持断点续传"""
        start = get_file_size(file_path)  # 从已下载的文件大小处开始

        # 获取文件总大小（如果需要的话，可以在这里发送一个HEAD请求来获取）
        # 但为了简单起见，这里假设服务器已经通过Content-Length头返回了总大小
        response = requests.head(url, allow_redirects=True)
        total_size = int(response.headers.get('content-length', 0))

        if total_size <= start:
            self.print_to_term("文件已下载完成！")
            return

        # 发送请求获取剩余文件块
        response = get_file_range(url, start)

        if response.status_code == 206:  # HTTP 206 Partial Content 表示请求成功
            with open(file_path, 'ab') as f:  # 以追加模式打开文件
                for chunk in response.iter_content(chunk_size=8192):
                    if chunk:
                        f.write(chunk)
                        # 这里可以添加代码来显示下载进度
                        downloaded_size = f.tell()
                        progress_percent = (downloaded_size / total_size) * 100
                        self.print_to_term(f"下载进度: {progress_percent:.2f}%", kind='info')

            self.print_to_term("下载完成！")
        else:
            self.print_to_term(f"文件下载失败，状态码:{response.status_code}")

    def do(self, action, args):
        if action == 'downloadFile':
            url = args
            self.download_file(url, './anaconda.exe')
        elif action == 'install':
            file = args


def test():
    api = Api()
    # print(api.get_key('platform'))
    # print(api.get_key('anaconda link'))
    # print(api.get_key('current version'))
    api.do('downloadFile',
           'https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2024.10-1-Windows-x86_64.exe')


if __name__ == '__main__':
    chinese = {
        'global.quitConfirmation': u'确定关闭?',
    }

    api = Api()
    # api.do('downloadFile',
    #    'https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2024.10-1-Windows-x86_64.exe')
    window = webview.create_window(
        title='gf-ui',
        url='./dist/index.html',
        width=900,
        height=620,
        resizable=True,  # 固定窗口大小
        text_select=True,  # 禁止选择文字内容
        confirm_close=True,  # 关闭时提示
        js_api=api,  # api中定义供html调用的函数
        min_size=(900, 620)  # 设置窗口最小尺寸
    )

    # 启动窗口
    # webview.start(localization=chinese, http_server=True, debug=True)
    # webview.start(custom_logic, window)  # 传入的函数会被立即执行
    webview.start(localization=chinese, ssl=True, debug=True)  # 该语句会阻塞，直到程序关闭后才会继续执行后续代码
